﻿///dbserver wikirs /db wikidb /dblogin root /dbpassword mysql /tableprefix meridian_ /scanonce
///dbserver wikirs /db wikidb /dblogin root /dbpassword mysql /tableprefix wikibeta_ /scanonce

using System;
using System.Data;
using System.Collections;
using MySql.Data.MySqlClient;

class AutoLinker
{
	public class Page
	{
		public Int32 pageId;
		public string pageTitle;
		public Int32 pageLatest;
		public Int32 pageNamespace;
		public ArrayList newLinkedPages = new ArrayList();
	}

	// Conversion parameters.
	string DBServer = null;
	string DB = null;
	string DBLogin = null;
	string DBPassword = null;
	string TablePrefix = null;
	bool ScanOnce = false;

	public ArrayList BreakOutParms(string Text)
	{
		ArrayList WorkingSectionList;
		bool inQuote;
		string WorkingText;

		Text = Text + " ";
		// Make sure we break at end
		WorkingText = "";
		inQuote = false;
		WorkingSectionList = new ArrayList();

		// Iterate through and break based on whitespaces, sans quoted things.
		foreach ( char ch in Text) {
			// Toggle for quotes.
			if ((ch == '\"'))
			{
				inQuote = !inQuote;
			}
			else
			{
				// In a quote - just copy chars over to the result.
				if ((inQuote))
				{
					WorkingText = WorkingText + ch;
				}
				// Break on a whitespace.
				else
				{
					if ((char.IsWhiteSpace(ch)))
					{
						WorkingSectionList.Add(WorkingText.Trim());
						WorkingText = "";
					}
					else
					{
						WorkingText = WorkingText + ch;
					}

				}

			}
		}
		return WorkingSectionList;

		// Return our split text.
	}

	public void MainLinkingProcess(bool ScanOnce)
	{
		string connStr;
		MySqlConnection conn;

		// Make sure we've got enough data to connect to the DB.
		if ((DBServer == null) | (DB == null) | (DBLogin == null) | (DBPassword == null) | (TablePrefix == null))
		{
			conn = null;
		}
		else
		{
			// Log into MySQL.
			System.Console.WriteLine("Logging into MySQL (db=" + DB + ") as " + DBLogin);
			connStr = string.Format("server={0};user id={1}; password={2}; database={3}; pooling=false", DBServer, DBLogin, DBPassword, DB);

			conn = new MySqlConnection(connStr);
			conn.Open();
			conn.ChangeDatabase(DB);
			//System.Console.WriteLine("Here!  z22aa")
		}

		// Keep doing this thing periodically.
		try
		{
			bool doneScanning = false;
			while (!doneScanning) 
			{
				// Do our deed!
				System.Console.WriteLine("--------------------------------------------");
				System.Console.WriteLine("Scan time: " + System.DateTime.Now.ToString());
				System.Console.WriteLine("--------------------------------------------");
				System.Console.WriteLine();
				PerformAutoLink(conn, TablePrefix);

				if (ScanOnce)
				{
					doneScanning = true;
				}
				else
				{
					// Wait for a short while before converting more.
					Int32 waitTime;
					waitTime = 1000 * 60 * 60;

					// 60 minutes
					System.Console.WriteLine("Going to sleep for a while...");
					System.Threading.Thread.Sleep(waitTime);
				}
			}
		}
		finally
		{
			// Log out of MySQL.
			if (((conn != null)))
			{
				System.Console.WriteLine("Logging out of MySQL");
				conn.Close();
			}
		}
	}

	public void PerformAutoLink(MySqlConnection conn, string TablePrefix)
	{
		MySqlDataReader reader;
		ArrayList pageList;
		bool autoLinkedAnything = false;

		// Get a list of articles.  We need to the full list for processing
		// each one individually next.
		System.Console.Write("Obtaining article list...");
		pageList = new ArrayList();
		reader = Query(conn, "select page_id,page_namespace,page_title,page_latest from " + GetTableName(TablePrefix, "page") + " where page_namespace=0" + " order by  CHAR_LENGTH(page_title) desc");

		while ((reader.Read())) {
			Page thisPage;

			thisPage = new Page();
			thisPage.pageId = reader.GetInt32(0);
			thisPage.pageNamespace = reader.GetInt32(1);
			thisPage.pageTitle = reader.GetString(2);
			thisPage.pageLatest = reader.GetInt32(3);

			pageList.Add(thisPage);
		}
		reader.Close();

		System.Console.WriteLine("Found " + pageList.Count.ToString() + " articles");

		// Iterate over all user articles.
		System.Console.WriteLine("Creating autolinks...");

		Int32 i;
		for (i = 0; i <= (pageList.Count - 1); i++) {
			Page thisPage;
			thisPage = (Page) pageList[i];

			if (AutoLinkOneArticle(conn, TablePrefix, pageList, thisPage))
			{
				autoLinkedAnything = true;
			}
		}

		// Delete the object cache if anything was changed.
		System.Console.WriteLine("Autolink complete!");
		if (autoLinkedAnything)
		{
			System.Console.WriteLine("Cleaning up object cache...");

			// Update the existing page record to show the page has been 'touched'.
			string SQL = "delete from " + GetTableName(TablePrefix, "objectcache");
			Exec(conn, SQL);
		}
	}

	public bool AutoLinkOneArticle(MySqlConnection conn, string TablePrefix, ArrayList pageList, Page thisPage)
	{
		bool functionReturnValue = false;
		MySqlDataReader reader;
		string SQL;
		Int32 TextId;
		string articleText;

		//System.Console.WriteLine(thisPage.pageTitle)
		functionReturnValue = false;

		// Find the TextID for the article contents.
		reader = Query(conn, "select rev_text_id from " + GetTableName(TablePrefix, "revision") + " where rev_id=" + thisPage.pageLatest.ToString());
		if ((!reader.Read()))
		{
			reader.Close();
			return false; // TODO: might not be correct. Was : Exit Function
		}
		TextId = reader.GetInt32(0);
		reader.Close();

		// Get the text of the page.
		reader = Query(conn, "select old_text from " + GetTableName(TablePrefix, "text") + " where old_id=" + TextId.ToString());
		if ((!reader.Read()))
		{
			reader.Close();
			return false; // TODO: might not be correct. Was : Exit Function
		}
		reader.Read();
		articleText = reader.GetString(0);
		reader.Close();

		//If (thisPage.pageTitle = "Sandbox") Then
		//thisPage.pageTitle = "Sandbox"
		//End If

		// Ignore articles of length=0.  For some reason deleted pages
		// sometime come across this way.
		if ((articleText.Trim().Length == 0))
		{
			return false; // TODO: might not be correct. Was : Exit Function
		}

		// See if this article is set up for autolinking.
		bool shouldAutoLink;
		shouldAutoLink = (articleText.IndexOf("[[autolink]]") > 0);

		if ((shouldAutoLink))
		{
			functionReturnValue = true;

			// Delete any existing links in the article, save the [[autolink]] tag.
			//SQL = String.Format("delete from " + GetTableName(TablePrefix, "pagelinks") _
			//     + " where pl_from=" + thisPage.pageId.ToString)
			//Exec(conn, SQL)
			//articleText = RemoveExistingLinks(articleText)

			// Time to generate some links!
			System.Console.WriteLine(thisPage.pageTitle);
			//System.Console.WriteLine("   - scanning for potential links")
			articleText = ParseArticleAndMakeLinks(pageList, thisPage, articleText);
			//System.Console.WriteLine("Done inserting links");

			// Now update the database with our changes
			SQL = string.Format("update " + GetTableName(TablePrefix, "text") + " set old_text=?text" + " where old_id=" + TextId.ToString());

			MySqlCommand cmd;
			MySqlParameter parmArticleText;

			cmd = conn.CreateCommand();
			cmd.CommandText = SQL;

			parmArticleText = cmd.CreateParameter();
			parmArticleText.ParameterName = "text";
			parmArticleText.Value = articleText;
			parmArticleText.MySqlDbType = MySqlDbType.String;

			cmd.Parameters.Add(parmArticleText);
			cmd.ExecuteNonQuery();

			// Add any new links to the links table.
			Int32 i;
			for (i = 0; i <= (thisPage.newLinkedPages.Count - 1); i++) {
				string refTitle;
				Int32 linkMatchCount;

				refTitle = (string) thisPage.newLinkedPages[i];

				// See if the link already exists.
				reader = Query(conn, "select count(1) from " + GetTableName(TablePrefix, "pagelinks") + " where pl_from=" + thisPage.pageId.ToString() + " and pl_title='" + refTitle + "'");
				reader.Read();
				linkMatchCount = reader.GetInt32(0);
				reader.Close();

				// Insert a link if we must!
				if ((linkMatchCount == 0))
				{
					SQL = string.Format("insert into " + GetTableName(TablePrefix, "pagelinks") + " (pl_from,pl_namespace,pl_title) " + " values(" + thisPage.pageId.ToString() + "," + thisPage.pageNamespace.ToString() + ",'" + refTitle + "')");

					cmd = conn.CreateCommand();
					cmd.CommandText = SQL;

					cmd.Parameters.Add(parmArticleText);
					cmd.ExecuteNonQuery();
				}
			}
		}

		// Update the existing page record to show the page has been 'touched'.
		//SQL = String.Format("update " + GetTableName(TablePrefix, "page") + " set " _
		//+ "page_title = '{0}'," _
		//+ "page_touched = {1}," _
		//+ "page_len = {2}" _
		//+ " where page_id = {3}", _
		//thisPage.pageTitle, GetCurrentDate(), Len(articleText), thisPage.pageId)

		//Exec(conn, SQL)
		else
		{
			//System.Console.WriteLine("   - not configured for auto-link")
		}
		return functionReturnValue;
	}

	public void PingDB(MySqlConnection conn)
	{
		string SQL;
		SQL = "select 1 from " + GetTableName(TablePrefix, "categorylinks");
		Exec(conn, SQL);
	}

	public string GetCurrentDate()
	{
		return DateTime.Now.ToString("yyyyMMddhhmmss");
	}

	public string GetEscapedArticleText(string ArticleText)
	{
		string buf = "";
		int i;
		char ch;

		// Look for special chars in the input.
		for (i = 0; i <= (ArticleText.Length - 1); i++) {
			ch = ArticleText[i];

			// Double up certain chars.
			if (ch == '\'')
			{
				buf = buf + ch;
			}
			buf = buf + ch;
		}

		// Set our normalized result.
		return buf.Trim();
	}

	public string RemoveNonAlphaNumeric(string ArticleText)
	{
		string buf = "";
		int i;
		char ch;

		// Look for special chars in the input.
		for (i = 0; i <= (ArticleText.Length - 1); i++) {
			ch = ArticleText[i];

			// Double up certain chars.
			if (char.IsLetterOrDigit(ch))
			{
				buf = buf + ch;
			}
			else
			{
				buf = buf + " ";
			}
		}

		// Set our normalized result.
		return buf.Trim();
	}

	public string GetTableName(string TablePrefix, string TableName)
	{
		return TablePrefix + TableName;
	}

	public MySqlDataReader Query(MySqlConnection conn, string QueryStr)
	{
		MySqlCommand cmd;

		cmd = conn.CreateCommand();
		cmd.CommandText = QueryStr;
		return cmd.ExecuteReader();
	}

	public int Exec(MySqlConnection conn, string SQL)
	{
		MySqlCommand cmd;

		cmd = conn.CreateCommand();
		cmd.CommandText = SQL;
		return cmd.ExecuteNonQuery();
	}

	public string BlankOutDelimitedText(string inputText, string startDelim, string endDelim)
	{
		int startDelimStartPos;
		int endDelimPos;
		string leftText;
		string rightText;
		string middleText;
		string outputText;

		//inputText = "yed|| [_imagemarker_ 5]" + Chr(10) + Chr(13) + "[_imagemarker_ 6]dsdsd"
		//startDelim = "["
		//endDelim = "]"

		// Look for an HTML comment.
		outputText = inputText;

		startDelimStartPos = outputText.IndexOf(startDelim);
		while ((startDelimStartPos > 0)) {
			// Find the end of the comment.
			endDelimPos = outputText.IndexOf(endDelim, startDelimStartPos + startDelim.Length);
			if ((endDelimPos > 0))
			{
				// Hack out the left and right of the comment.
				leftText = GetStringLeft(outputText, startDelimStartPos - 1);
				rightText = GetStringRight(outputText, outputText.Length - endDelimPos - endDelim.Length + 1);

				// Make a string of spaces the same size as the cut string.
				// This keeps things positionally stable.
				int spcCount;
				spcCount = endDelimPos - startDelimStartPos + endDelim.Length;

				middleText = "";
				while ((spcCount > 0)) {
					middleText = middleText + " ";
					spcCount = spcCount - 1;
				}

				// Reassemble without the comment.
				outputText = leftText + middleText + rightText;
			}

			// Look for the next HTML comment.
			startDelimStartPos = outputText.IndexOf(startDelim, startDelimStartPos + 1);
		}

		// Sanity check to make sure input and output are the same length!
		if ((inputText.Length != outputText.Length))
		{
			//System.Console.WriteLine(inputText)
			//System.Console.WriteLine(outputText)

			throw new Exception("BlankOutDelimitedText() produced a positionally-unstable result");
		}
		return outputText;

	}

	public string ParseArticleAndMakeLinks(ArrayList pageList, Page srcPage, string articleText)
	{
		// Iterate over each article.
		Int32 i;
		for (i = 0; i <= (pageList.Count - 1); i++) {
			Page destPage;
			destPage = (Page) pageList[i];

			// Do replacements on instances of this article.
			// Don't ever link an article back to itself.
			if ((destPage.pageTitle != srcPage.pageTitle))
			{
				string destTitleNormalized;
				destTitleNormalized = destPage.pageTitle.Replace("_", " ");

				//If (destTitleNormalized = "SOA") Then
				//destTitleNormalized = "SOA"
				//End If
				articleText = InsertLinkToArticle(articleText, destTitleNormalized, srcPage.newLinkedPages);
			}
		}
		return articleText;

		// Return what we've concocted.
	}

	public string InsertLinkToArticle(string articleText, string pageToReference, ArrayList newLinkedPages)
	{
		// Quick check to see if there is any reference even close.
		if ((articleText.ToLower().IndexOf(pageToReference.ToLower()) < 0))
		{
			return articleText; 
		}

		// Keep looping while there are still replacements to be made.
		bool keepLooping;
		keepLooping = true;

		while ((keepLooping)) {
			// Make a positionally-stable list with all special wiki thingies removed.
			string filteredArticleText;

			filteredArticleText = BlankOutDelimitedText(articleText, "[[", "]]");
			filteredArticleText = BlankOutDelimitedText(filteredArticleText, "[", "]");
			filteredArticleText = BlankOutDelimitedText(filteredArticleText, "{{", "}}");
			filteredArticleText = BlankOutDelimitedText(filteredArticleText, "<", ">");
			filteredArticleText = BlankOutDelimitedText(filteredArticleText, "=======", "=======");
			filteredArticleText = BlankOutDelimitedText(filteredArticleText, "======", "======");
			filteredArticleText = BlankOutDelimitedText(filteredArticleText, "=====", "=====");
			filteredArticleText = BlankOutDelimitedText(filteredArticleText, "====", "====");
			filteredArticleText = BlankOutDelimitedText(filteredArticleText, "===", "===");
			filteredArticleText = BlankOutDelimitedText(filteredArticleText, "==", "==");
			filteredArticleText = BlankOutDelimitedText(filteredArticleText, "=", "=");

			// Look for an instance of the article title.  Must be preceded by a space
			// and trailed by a space or punctuation.
			ArrayList possibleCombinations = new ArrayList();
			string scanText;
			string scanRefText;
			Int32 i;

			// Less than four chars - must match case too.  This keeps from
			// linking things like 'it' to pages called 'IT'.
			if ((pageToReference.Length >= 4))
			{
				filteredArticleText = filteredArticleText.ToLower();
				scanRefText = pageToReference.ToLower();
			}
			else
			{
				//filteredArticleText = filteredArticleText
				scanRefText = pageToReference;
			}
			scanText = " " + filteredArticleText;

			// Create a list of valid delimited variants.
			ArrayList wordDelimiters = new ArrayList();
			ArrayList wordSuffixes = new ArrayList();
			ArrayList validVariants = new ArrayList();

			wordDelimiters.Add(" ");
			wordDelimiters.Add("\r");
			wordDelimiters.Add("\n");
			wordDelimiters.Add("|");
			wordDelimiters.Add(".");
			wordDelimiters.Add("?");
			wordDelimiters.Add("!");
			wordDelimiters.Add(";");
			wordDelimiters.Add(":");
			wordDelimiters.Add(",");
			wordDelimiters.Add("-");
			wordDelimiters.Add("'");
			wordDelimiters.Add("\"");

			wordSuffixes.Add("es");
			wordSuffixes.Add("s");
			wordSuffixes.Add("");

			Int32 j;
			Int32 k;
			Int32 l;
			for (j = 0; j <= (wordDelimiters.Count - 1); j++) {
				for (k = 0; k <= (wordSuffixes.Count - 1); k++) {
					for (l = 0; l <= (wordDelimiters.Count - 1); l++) {
						object preDelim = wordDelimiters[j];
						object wordSuffix = wordSuffixes[k];
						object postDelim = wordDelimiters[l];

						possibleCombinations.Add(preDelim + scanRefText + wordSuffix + postDelim);
					}
				}
			}

			// Look for any matches.  Find the first one that appears in the 
			// so the link is not at some seemingly random point within the 
			// occurrence set.
			bool foundMatch = false;
			Int32 lowestInstancePos = Int32.MaxValue;
			string scanMatchText = "";

			for (i = 0; i <= (possibleCombinations.Count - 1); i++) {
				string tryThis = (string) possibleCombinations[i];

				Int32 instancePos = scanText.IndexOf(tryThis);
				if ((instancePos > 0))
				{
					// Convert to pos in original string, not scan text
					foundMatch = true;
					scanMatchText = tryThis;
					instancePos = instancePos - 1;

					// Wait for first match in the document.
					lowestInstancePos = Math.Min(lowestInstancePos, instancePos);
				}
			}

			if ((foundMatch))
			{
				// Find the edges based on edge delimiters.
				ArrayList edgeDelimiterList = new ArrayList();
				edgeDelimiterList.Add(" ");
				edgeDelimiterList.Add("\r");
				edgeDelimiterList.Add("\n");
				edgeDelimiterList.Add("|");
				edgeDelimiterList.Add(".");
				edgeDelimiterList.Add("?");
				edgeDelimiterList.Add("!");
				edgeDelimiterList.Add(";");
				edgeDelimiterList.Add(":");
				edgeDelimiterList.Add(",");
				edgeDelimiterList.Add("-");
				edgeDelimiterList.Add("'");
				edgeDelimiterList.Add("\"");

				// The "edge" returned includes the delimiters themselves.
				Int32 leftEdge = FindTextEdge(filteredArticleText, lowestInstancePos, edgeDelimiterList, -1);
				Int32 rightEdge = FindTextEdge(filteredArticleText, lowestInstancePos + pageToReference.Length - 1, edgeDelimiterList, 1);

				string actualMatch = articleText.Substring(leftEdge + 1, rightEdge - leftEdge);

				// Process a match!
				if ((rightEdge > leftEdge))
				{
					// If the link already exists on this page, don't add it again.
					if (((articleText.IndexOf("[[" + pageToReference + "]]") < 0) & (articleText.IndexOf("[[" + pageToReference + "|") < 0)))
					{
						// Sanity check - make sure the phrase appears in the actual match text.
						if (((" " + actualMatch.ToLower()).IndexOf(pageToReference.ToLower()) <= 0))
						{
							System.Console.WriteLine("Internal error: reference <" + actualMatch + "> does not contain '" + pageToReference + "' as expected");
							return articleText;
						}

						// Hack out the left and right of the ref.  Also the ref itself.
						string leftText;
						string rightText;
						string middleText;
						string pageReference;

						leftText = GetStringLeft(articleText, leftEdge + 1);
						rightText = GetStringRight(articleText, articleText.Length - rightEdge - 1);
						middleText = articleText.Substring(leftEdge + 1, rightEdge - leftEdge);

						// Put the ref back in, this time with a link.
						if ((middleText == pageToReference))
						{
							pageReference = "[[" + pageToReference + "]]";
						}
						else
						{
							pageReference = "[[" + pageToReference + "|" + middleText + "]]";
						}

						System.Console.WriteLine("   - Adding link from <" + middleText + "> to '" + pageToReference + "'");

						articleText = leftText + pageReference + rightText;

						// Record the link in our ArrayList xref.
						if ((!newLinkedPages.Contains(pageToReference)))
						{
							newLinkedPages.Add(pageToReference);
							keepLooping = false;
						}
					}
					else
					{
						keepLooping = false;
					}
				}
				else
				{
					keepLooping = false;
				}
			}
			else
			{
				keepLooping = false;
			}
		}
		return articleText;

		// Return what we've concocted.
	}

	public Int32 FindTextEdge(string text, Int32 startingPoint, ArrayList edgeDelimiterList, Int32 direction)
	{

		Int32 scanIdx = startingPoint;
		Int32 edgeIdx = startingPoint;

		// Looping until we've hit the edge of the text.
		while ((scanIdx >= 0) & (scanIdx < text.Length)) {
			char ch = text[scanIdx];

			// See if we've found the edge.
			if ((edgeDelimiterList.Contains(ch.ToString())))
			{
				break; // TODO: might not be correct. Was : Exit While
			}
			else
			{
				edgeIdx = scanIdx;
			}

			// Move to the next char.
			scanIdx = scanIdx + direction;
		}
		return edgeIdx;

	}

	public string RemoveExistingLinks(string articleText)
	{
		return articleText;
	}

	public static string GetStringLeft(string val, int charCount)
	{
		if (val.Length > charCount)
			return val.Substring(0, charCount);
		else
			return val;
	}

	public static string GetStringRight(string val, int charCount)
	{
		if (val.Length > charCount)
			return val.Substring(val.Length - charCount, charCount);
		else
			return val;
	}

	public static void Main(string[] ArgList)
	{
		System.Console.WriteLine();

		System.Console.WriteLine("=======================================================================");
		System.Console.WriteLine("WikiAutoLink CrossLink Generator - For MediaWiki");
		System.Console.WriteLine("Version 1.0a");
		System.Console.WriteLine("=======================================================================");
		System.Console.WriteLine();

		bool showHelp;
		if (ArgList.Length == 0) 
		{
			showHelp = true;
		}
		else if ((ArgList.Length == 1) && (ArgList[0] == "/?"))
		{
			showHelp = true;
		}
		else
		{
			showHelp = false;
		}

		if (showHelp)
		{
			System.Console.WriteLine("Scans articles in a Wiki and automatically creates links based upon");
			System.Console.WriteLine("keywords and titles.");
			System.Console.WriteLine();
			System.Console.WriteLine("Usage:");
			System.Console.WriteLine("WikiAutoLink");
			System.Console.WriteLine("  [/dbserver xxxx]     - Server that is hosting the wiki's MySQL database");
			System.Console.WriteLine("  [/db xxxx]           - Name of the wiki's database on the server");
			System.Console.WriteLine("  [/dblogin xxxx]      - Login name for the database");  
			System.Console.WriteLine("  [/dbpassword xxxx]   - Password for logging into the database");
			System.Console.WriteLine("  [/tableprefix xxxx]  - Prefix applied to all wiki tables");
			System.Console.WriteLine("  [/scanonce]          - Run only once rather than scanning in a continual fashion");
			System.Console.WriteLine();
			System.Console.WriteLine("Example:");
			System.Console.WriteLine("/dbserver wikisvr /db wikidb /dblogin root /dbpassword rootpw /tableprefix wikibeta_ /scanonce");
		}
		else
		{
			try 
			{
				// Construct our linker instance.
				AutoLinker linker = new AutoLinker();

				// Parse the argument list.  Iterate over each parm.
				System.Console.WriteLine("[Debug]");
				string NextParm = null;
				foreach ( string Arg in ArgList) 
				{
					System.Console.WriteLine("Arg=" + Arg);

					// Look for any simple args.
					if ((Arg == "/scanonce"))
					{
						linker.ScanOnce = true;
					}

					// See if the value is second part of a tagged one.
					// The preceding parm has a  '/' prefix).
					if ((NextParm == "dbserver"))
					{
						linker.DBServer = Arg;
					}

					if ((NextParm == "db"))
					{
						linker.DB = Arg;
					}

					if ((NextParm == "dblogin"))
					{
						linker.DBLogin = Arg;
					}

					if ((NextParm == "dbpassword"))
					{
						linker.DBPassword = Arg;
					}

					if ((NextParm == "tableprefix"))
					{
						linker.TablePrefix = Arg;
					}

					NextParm = null;

					// look for prefixes and store what type for the next iteration.
					if (GetStringLeft(Arg, 1) == "/")
					{
						NextParm = GetStringRight(Arg, Arg.Length - 1);
					}
				}

				// Dump out our final config info for debugging purposes.
				System.Console.WriteLine("");
				System.Console.WriteLine("DBServer:    " + linker.DBServer);
				System.Console.WriteLine("DB:          " + linker.DB);
				System.Console.WriteLine("DBLogin:     " + linker.DBLogin);
				System.Console.WriteLine("DBPassword:  " + linker.DBPassword);
				System.Console.WriteLine("TablePrefix: " + linker.TablePrefix);
				System.Console.WriteLine("ScanOnce:    " + linker.ScanOnce);
				System.Console.WriteLine();

				// Convert away!
				linker.MainLinkingProcess(linker.ScanOnce);
			}
			catch (Exception ex) 
			{
				System.Console.WriteLine();
				System.Console.WriteLine("Error!");
				System.Console.WriteLine(ex.Message);
				System.Console.WriteLine();
				System.Console.WriteLine(ex.StackTrace);
			}
		}
	}
}
